package com.pan.download3;

import com.pan.crawler.Directory;
import com.pan.crawler.Page;
import com.pan.crawler.impl.DirectoryImpl;
import com.pan.crawler.impl.PageImpl;
import com.pan.download3.ctrl.BlockCtrl;
import com.pan.download3.ctrl.TaskCtrl;
import com.pan.download3.ctrl.WriteCtrl;
import com.pan.entity.BookDirectory;
import com.pan.entity.Chapter;
import com.pan.entity.ThreadTask;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Driver {


    private BlockCtrl blockCtrl;
    private TaskCtrl taskCtrl;
    private WriteCtrl writeCtrl;
    private int threadCount;
    private ThreadPoolExecutor poolExecutor;

    public Driver(){
        int cpuCount = Runtime.getRuntime().availableProcessors();
        threadCount = cpuCount * 5;
        this.poolExecutor = new ThreadPoolExecutor(threadCount,threadCount,10, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }
    public Driver(int threadCount){
        this.threadCount = threadCount;
        this.poolExecutor = new ThreadPoolExecutor(threadCount,threadCount,10, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }
    /**
     *
     * @param url 这个是目录的地址
     */
    public void run(String url){
        this.run(url,null,PageImpl.class,DirectoryImpl.class);
    }

    /**
     *
     * @param url  目录地址
     * @param pageClass 你实现的page接口的类的Class对象
     * @param directoryClass 你实现的directory接口的类的Class对象
     */
    public void run(String url,Class pageClass,Class directoryClass){
        this.run(url,null,pageClass,directoryClass);
    }

    public void run(String url,String saveDirPath,Class pageClass,Class directoryClass){
        Directory directory;
        List<Page> pageList = new ArrayList<>();
        try {
            Object d= directoryClass.newInstance();
            Object p = pageClass.newInstance();
            if (!(d instanceof Directory )|| !(p instanceof Page)){
                throw new RuntimeException("传入的Class对象没有实现Directory或者Page接口");
            }
            directory = (Directory) d;
            pageList.add((Page) p);
            for (int i = 0; i < threadCount-1; i++) {
                pageList.add((Page) pageClass.newInstance());
            }
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        BookDirectory bookDirectory = directory.get(url);
        this.taskCtrl = new TaskCtrl(bookDirectory.getChapterList());
        this.blockCtrl = new BlockCtrl();
        this.writeCtrl = new WriteCtrl(bookDirectory.getBookName(),saveDirPath,this.blockCtrl);
        for (int i = 0; i < threadCount; i++) {
            Page page = pageList.get(i);
            poolExecutor.execute(()->{
//                还是不要用这个了很多小说网站是异步请求小说内容的
//                EdgeOptions edgeOptions = new EdgeOptions();
//                edgeOptions.setPageLoadStrategy(PageLoadStrategy.EAGER);
                while (true) {
                    Chapter chapter = taskCtrl.getTask();
                    if (chapter==null) break;
                    String content = "";
                    try {
                        content = page.get(chapter);
                    }catch (Exception e){
                        e.printStackTrace();
                        taskCtrl.cancelTask(chapter);
                        continue;
                    }
                    if (!writeCtrl.append(content,chapter.getOrderNumber())){
                        blockCtrl.block(new ThreadTask(chapter,Thread.currentThread()));
                        writeCtrl.append(content,chapter.getOrderNumber());
                    }
                    System.out.println("下载成功："+chapter.toString());
                }
                page.close();

            });
        }
        poolExecutor.shutdown();
    }

}
